// WinFile.cpp: implementation of the File class, taken from MFC :)
//
//////////////////////////////////////////////////////////////////////

#include "xort.h"
#include <windows.h>

int File::Open( string csFileName, dword dwOpenFlags )
{
    dwOpenFlags &= ~OF_typeBinary;
    m_hFile = INVALID_HANDLE_VALUE;
    m_strFileName.Assign( csFileName );
    
    DWORD dwAccess = 0;
    switch( dwOpenFlags & 3 )
    {
    case OF_modeRead:
        dwAccess = GENERIC_READ;
        break;
    case OF_modeWrite:
        dwAccess = GENERIC_WRITE;
        break;
    case OF_modeReadWrite:
        dwAccess = GENERIC_READ|GENERIC_WRITE;
        break;
    default:
        return 0;  // invalid share mode
    }

    // map share mode
    DWORD dwShareMode = 0;
    switch( dwOpenFlags & 0x70 ) // map compatibility mode to exclusive
    {
    case OF_shareCompat:
    case OF_shareExclusive:
        dwShareMode = 0;
        break;
    case OF_shareDenyWrite:
        dwShareMode = FILE_SHARE_READ;
        break;
    case OF_shareDenyRead:
        dwShareMode = FILE_SHARE_WRITE;
        break;
    case OF_shareDenyNone:
        dwShareMode = FILE_SHARE_WRITE|FILE_SHARE_READ;
        break;
    }

    // map modeNoInherit flag
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = (dwOpenFlags & OF_modeNoInherit) == 0;

    // map creation flags
    DWORD dwCreateFlag;
    if (dwOpenFlags & OF_modeCreate)
    {
        if (dwOpenFlags & OF_modeNoTruncate)
            dwCreateFlag = OPEN_ALWAYS;
        else
            dwCreateFlag = CREATE_ALWAYS;
    }
    else
        dwCreateFlag = OPEN_EXISTING;

    // attempt file creation
    HANDLE hFile = ::CreateFileA( m_strFileName.GetBuffer(), dwAccess, dwShareMode, &sa,
        dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL );
    if( hFile == INVALID_HANDLE_VALUE )
    {
        m_strFileName.Empty();
        return 0;
    } else {
        string csFullPathName;
        if( csFullPathName.AllocBuffer( MAX_PATH*2 ) )
        {
            LPSTR lpszFilePart;
            if( GetFullPathNameA( m_strFileName.GetBuffer(), MAX_PATH*2, csFullPathName.GetBuffer(), &lpszFilePart ) )
                m_strFileName.Assign( csFullPathName );
            // else remains short name.
        }
    }

    m_hFile = hFile;
    return 1;
}

int File::isOpened()
{
    if( m_hFile == INVALID_HANDLE_VALUE )
        return 0;
    return 1;
}

dword File::Length()
{
    if( m_hFile == INVALID_HANDLE_VALUE )
        return 0;

    DWORD dwHiSize;
    DWORD dwSize = GetFileSize( m_hFile, &dwHiSize );

    return dwSize;
}

dword File::Read( BYTEARRAY buf, dword dwCount )
{
    if( m_hFile == INVALID_HANDLE_VALUE || dwCount == 0 )
        return 0;

    buf.zero();
    if( !buf.reserve( dwCount ) )
        return 0;

    DWORD dwRead;
    if( !::ReadFile( m_hFile, buf.ptr(0), dwCount, &dwRead, NULL ) )
    {
        dwRead = 0;
    }

    buf.set_itemsCount( dwRead );

    return dwRead;
}

dword File::WriteByteArray( BYTEARRAY buf )
{
    if( m_hFile == INVALID_HANDLE_VALUE || buf.count() == 0 )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, buf.ptr(0), buf.count(), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

dword File::WriteDwordArray( DWORDARRAY buf )
{
    if( m_hFile == INVALID_HANDLE_VALUE || buf.count() == 0 )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, buf.ptr(0), buf.count() * sizeof(dword), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

dword File::WriteByte( byte bVal )
{
    if( m_hFile == INVALID_HANDLE_VALUE )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, &bVal, sizeof(byte), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

dword File::WriteDword( dword dwVal )
{
    if( m_hFile == INVALID_HANDLE_VALUE )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, &dwVal, sizeof(dword), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

dword File::WriteQword( qword qwVal )
{
    if( m_hFile == INVALID_HANDLE_VALUE )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, &qwVal, sizeof(qword), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

dword File::WriteInt( int iVal )
{
    if( m_hFile == INVALID_HANDLE_VALUE )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, &iVal, sizeof(int), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

dword File::WriteString( string csStr )
{
    if( m_hFile == INVALID_HANDLE_VALUE || csStr.Length() == 0 )
        return 0;

    DWORD dwWritten;
    if( !::WriteFile( m_hFile, csStr.GetBuffer(), csStr.Length(), &dwWritten, NULL ) )
    {
        return 0;
    }

    return dwWritten;
}

string File::ReadString( dword dwLength )
{
    string csRet;
    if( m_hFile == INVALID_HANDLE_VALUE )
        return csRet;

    if( !csRet.AllocBuffer( dwLength ) )
        return csRet;

    DWORD dwRead;
    char * p = csRet.GetBuffer();
    if( !::ReadFile( m_hFile, p, dwLength, &dwRead, NULL ) )
    {
        dwRead = 0;
    }
    
    p[dwRead] = 0;

    return csRet;
}

byte File::ReadByte()
{
    byte bRet = 0;
    if( m_hFile == INVALID_HANDLE_VALUE )
        return bRet;

    DWORD dwRead;
    if( !::ReadFile( m_hFile, &bRet, sizeof(byte), &dwRead, NULL ) )
    {
        dwRead = 0;
    }

    return bRet;
}

dword File::ReadDword()
{
    dword dwRet = 0xCDCDCDCD;
    if( m_hFile == INVALID_HANDLE_VALUE )
        return dwRet;

    DWORD dwRead;
    if( !::ReadFile( m_hFile, &dwRet, sizeof(dword), &dwRead, NULL ) )
    {
        dwRead = 0;
    }

    return dwRet;
}

dword File::ReadInt()
{
    int iRet = -1;
    if( m_hFile == INVALID_HANDLE_VALUE )
        return iRet;

    DWORD dwRead;
    if( !::ReadFile( m_hFile, &iRet, sizeof(int), &dwRead, NULL ) )
    {
        dwRead = 0;
    }

    return iRet;
}

qword File::ReadQword()
{
    qword qwRet = 0xCDCDCDCDCDCDCDCD;
    if( m_hFile == INVALID_HANDLE_VALUE )
        return qwRet;

    DWORD dwRead;
    if( !::ReadFile( m_hFile, &qwRet, sizeof(qword), &dwRead, NULL ) )
    {
        dwRead = 0;
    }

    return qwRet;
}

dword File::ReadDwordArray( DWORDARRAY buf, dword dwCount )
{
    if( m_hFile == INVALID_HANDLE_VALUE || dwCount == 0 )
        return 0;

    buf.zero();
    if( !buf.reserve( dwCount ) )
        return 0;

    DWORD dwRead;
    if( !::ReadFile( m_hFile, buf.ptr(0), dwCount * sizeof(dword), &dwRead, NULL ) )
    {
        dwRead = 0;
    }

    buf.set_itemsCount( dwCount );

    return dwRead;
}

void File::Close()
{
    if( m_hFile != INVALID_HANDLE_VALUE )
    { 
        ::CloseHandle( m_hFile );
        m_hFile = INVALID_HANDLE_VALUE;
        m_strFileName.Empty();
    }
}

/*
LONG File::Seek(LONG lOff, UINT nFrom)
{
    ATLASSERT(m_hFile != INVALID_HANDLE_VALUE);
    ATLASSERT(nFrom == begin || nFrom == end || nFrom == current);
    ATLASSERT(begin == FILE_BEGIN && end == FILE_END && current == FILE_CURRENT);
    
    DWORD dwNew = ::SetFilePointer( m_hFile, lOff, NULL, (DWORD)nFrom );
    if( dwNew  == (DWORD)-1 )
        return 0;

    return dwNew;
}

DWORD File::GetPosition() const
{
    ATLASSERT(m_hFile != INVALID_HANDLE_VALUE);
    
    DWORD dwPos = ::SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT );
    if( dwPos  == (DWORD)-1 )
        return 0;
    
    return dwPos;
}

void File::Flush()
{
    if (m_hFile == INVALID_HANDLE_VALUE)
        return;
    
    if( !::FlushFileBuffers( m_hFile ) )
    {
        ATLTRACE( _T("File::Flush -> FlushFileBuffers failed! OS error: [%d]\n"), ::GetLastError() );
    }
}

void File::Abort()
{
    if( m_hFile != INVALID_HANDLE_VALUE )
    {
        // close but ignore errors
        ::CloseHandle( m_hFile );
        m_hFile = INVALID_HANDLE_VALUE;
    }

    m_strFileName.Empty();
}

BOOL File::LockRange( DWORD dwPos, DWORD dwCount )
{
    ATLASSERT(m_hFile != INVALID_HANDLE_VALUE);
    
    if( !::LockFile( m_hFile, dwPos, 0, dwCount, 0 ) )
    {
        ATLTRACE( _T("File::LockRange -> LockFile failed! OS error: [%d]\n"), ::GetLastError() );
        return FALSE;
    }

    return TRUE;
}

BOOL File::UnlockRange( DWORD dwPos, DWORD dwCount )
{
    ATLASSERT(m_hFile != INVALID_HANDLE_VALUE);
    
    if( !::UnlockFile( m_hFile, dwPos, 0, dwCount, 0 ) )
    {
        ATLTRACE( _T("File::UnlockRange -> UnlockFile failed! OS error: [%d]\n"), ::GetLastError() );
        return FALSE;
    }

    return TRUE;
}

BOOL File::SetLength(DWORD dwNewLen)
{
    ATLASSERT(m_hFile != INVALID_HANDLE_VALUE);
    
    Seek((LONG)dwNewLen, (UINT)begin);
    
    if( !::SetEndOfFile((HANDLE)m_hFile) )
    {
        ATLTRACE( _T("File::SetLength -> SetEndOfFile failed! OS error: [%d]\n"), ::GetLastError() );
        return FALSE;
    }

    return TRUE;
}

BOOL PASCAL File::Rename( LPCOLESTR lpszOldName, LPCOLESTR lpszNewName )
{
    if( !::MoveFileW( lpszOldName, lpszNewName ) )
    {
        ATLTRACE( _T("File::Rename -> MoveFileW failed! OS error: [%d]\n"), ::GetLastError() );
        return FALSE;
    }

    return TRUE;
}

BOOL PASCAL File::Remove( LPCOLESTR lpszFileName )
{
    if( !::DeleteFileW( lpszFileName ) )
    {
        ATLTRACE( _T("File::Remove -> DeleteFileW failed! OS error: [%d]\n"), ::GetLastError() );
        return FALSE;
    }

    return TRUE;
}

BOOL PASCAL File::IsFileExist( LPCOLESTR lpstrFileName )
{
    ATLASSERT( lpstrFileName );

    DWORD dwAttr = ::GetFileAttributesW( lpstrFileName );

    if( dwAttr == -1 )
        return FALSE;

    if( dwAttr & FILE_ATTRIBUTE_DIRECTORY )
        return FALSE;

    return TRUE;
}

BOOL PASCAL File::GetTempFolder( BSTR *pbsTempFodler )
{
    if( !pbsTempFodler )
        return FALSE;
    
    WCHAR szPath[MAX_PATH];
    
    if( GetTempPathW( MAX_PATH, szPath ) )
    {
        *pbsTempFodler = SysAllocString( szPath );
        return TRUE;
    }

    return FALSE;
}

BOOL PASCAL File::GetTempFileName( BSTR *pbsTempFile )
{
    if( !pbsTempFile )
        return FALSE;

    WCHAR szPath[MAX_PATH];
    WCHAR szFileName[MAX_PATH];

    if( ::GetTempPathW( MAX_PATH, szPath ) )
    {
        if( ::GetTempFileNameW( szPath, L"YFBACZAFEBADF", 0, szFileName ) )
        {
            *pbsTempFile = SysAllocString( szFileName );
            return TRUE;
        }
    }

    return FALSE;
}

BOOL PASCAL File::GetTempFileName( LPCOLESTR lpstrLocation, LPCOLESTR lpstrExt, BSTR *pbsTempFile )
{
    if( !lpstrLocation || !pbsTempFile )
        return FALSE;

    WCHAR szFileName[MAX_PATH];

    if( GetTempFileNameW( lpstrLocation, L"DFGHRTUWOIQWUYER", 0, szFileName ) )
    {
        if( lpstrExt )
        {
            ::DeleteFileW( szFileName );

            WCHAR drive[_MAX_DRIVE];
            WCHAR dir[_MAX_PATH];
            WCHAR fname[_MAX_FNAME];
            WCHAR ext[_MAX_EXT];
            
            _wsplitpath( szFileName, drive, dir, fname, ext );
            _wmakepath( szFileName, drive, dir, fname, lpstrExt );
        }

        *pbsTempFile = SysAllocString( szFileName );
        return TRUE;
    }

    return FALSE;
}

BOOL PASCAL File::Copy( LPCOLESTR lpstrSrc, LPCOLESTR lpstrDst, BOOL bOverwrite, BOOL bDeleteSrc )
{
    if( !lpstrSrc || !lpstrDst )
        return FALSE;

    if( CopyFileW( lpstrSrc, lpstrDst, !bOverwrite ) )
    {
        if( bDeleteSrc )
        {
            DeleteFileW( lpstrSrc );
        }

        return TRUE;
    }

    return FALSE;
}

BOOL PASCAL File::ChangeExtension( LPCOLESTR lpstrFilePathName, LPCOLESTR lpstrExt, BSTR *pbsResultPath )
{
    if( !lpstrFilePathName || !lpstrExt )
        return FALSE;

    WCHAR szPath[_MAX_PATH];
    WCHAR drive[_MAX_DRIVE];
    WCHAR dir[_MAX_PATH];
    WCHAR fname[_MAX_FNAME];
    WCHAR ext[_MAX_EXT];
    
    _wsplitpath( lpstrFilePathName, drive, dir, fname, ext );
    _wmakepath( szPath, drive, dir, fname, lpstrExt );

    if( Rename( const_cast<LPOLESTR>( lpstrFilePathName ), szPath ) )
    {
        if( pbsResultPath )
            *pbsResultPath = ::SysAllocString( szPath );

        return TRUE;
    }
    return FALSE;
}

BOOL PASCAL File::IsFolderExist( LPCOLESTR lpstrFolder )
{
    if( !lpstrFolder )
        return FALSE;

    DWORD dwAttr = ::GetFileAttributesW( lpstrFolder );

    if( dwAttr == INVALID_FILE_ATTRIBUTES )
        return FALSE;

    if( dwAttr & FILE_ATTRIBUTE_DIRECTORY )
        return TRUE;
    return FALSE;
}

BOOL PASCAL File::CreateFolder( LPCOLESTR lpstrFolder )
{
    if( !lpstrFolder )
        return FALSE;

    if( !::CreateDirectoryW( lpstrFolder, NULL ) )
        return FALSE;
    return TRUE;
}

BOOL PASCAL File::CopyFileToFolder( LPCOLESTR lpstrFile, LPCOLESTR lpstrFolder, BSTR *psFinalFileName )
{
    ATLASSERT( lpstrFile );
    ATLASSERT( lpstrFolder );

    if( IsFileExist( lpstrFile ) )
    {
        if( !IsFolderExist( lpstrFolder ) &&
            !::CreateDirectoryW( lpstrFolder, NULL ) )
        {
            ATLTRACE( _T("File::CopyFileToFolder -> can't create destination folder!\n") );
            return FALSE;
        }
        else
        {
            WCHAR path_buffer[_MAX_PATH];
            WCHAR drive[_MAX_DRIVE];
            WCHAR dir[_MAX_PATH];
            WCHAR fname[_MAX_FNAME];
            WCHAR ext[_MAX_EXT];
            
            _wsplitpath( lpstrFile, drive, dir, fname, ext );
            _wmakepath( path_buffer, L"", lpstrFolder, fname, ext );

            if( ::CopyFileW( lpstrFile, path_buffer, FALSE ) )
            {
                if( psFinalFileName)
                    *psFinalFileName = ::SysAllocString( path_buffer );
                return TRUE;
            }
        }
    }

    return FALSE;
}

BOOL PASCAL File::GetStatus( LPCOLESTR lpszFileName, CWinFileStatus& rStatus )
{
    WIN32_FILE_ATTRIBUTE_DATA fd;
    if( !GetFileAttributesExW( lpszFileName, GetFileExInfoStandard, &fd ) )
    {
        ATLTRACE( _T("GetFileAttributesEx Failed!, os error: [%d]\n"), GetLastError() );
        return FALSE;
    }
    
    // strip attribute of NORMAL bit, our API doesn't have a "normal" bit.
    rStatus.m_attribute = (BYTE)( fd.dwFileAttributes & ~FILE_ATTRIBUTE_NORMAL );
    
    // get just the low DWORD of the file size
    ATLASSERT( fd.nFileSizeHigh == 0 );
    rStatus.m_size = fd.nFileSizeLow;
    
    // convert times as appropriate
    rStatus.m_ctime = FileTime2VariantDate( &fd.ftCreationTime );
    rStatus.m_atime = FileTime2VariantDate( &fd.ftLastAccessTime );
    rStatus.m_mtime = FileTime2VariantDate( &fd.ftLastWriteTime );
    
	return TRUE;
}

DATE PASCAL File::FileTime2VariantDate( const FILETIME *lpFTime )
{
    ATLASSERT( lpFTime );
    DATE dDate;
    SYSTEMTIME st;

    FileTimeToSystemTime( lpFTime, &st );
    SystemTimeToVariantTime( &st, &dDate );
    return dDate;
}

BOOL PASCAL File::GetFileVersion( LPCOLESTR lpstrFilePath, DWORD *pdwMaj, DWORD *pdwMin, DWORD *pdwRev, DWORD *pdwBuild )
{
    DWORD dwSize, dwVal, dwMajor, dwMinor, dwRevision, dwBuild;
    dwSize = ::GetFileVersionInfoSizeW( const_cast<LPOLESTR>( lpstrFilePath ), &dwVal );

    if( dwSize && !dwVal )
    {
        BYTE* pFileVersionBuffer = new BYTE[dwSize];
        if( ::GetFileVersionInfoW( const_cast<LPOLESTR>( lpstrFilePath ), NULL, dwSize, (void*) pFileVersionBuffer ) )
        {
            LPTSTR pszInfo = NULL;

            CString Major, Minor, Revision, Build;

            struct LANGANDCODEPAGE {
                WORD wLanguage;
                WORD wCodePage;
            } *lpTranslate;

            DWORD dwTranslate = sizeof(LANGANDCODEPAGE);
            DWORD dwLenCount;
            // gettig language information
            VerQueryValue(pFileVersionBuffer, 
              _T("\\VarFileInfo\\Translation"),
              (LPVOID*)&lpTranslate,
              (UINT*) &dwLenCount);

            for( DWORD i=0; i < dwLenCount/sizeof(struct LANGANDCODEPAGE); i++)
            {
                CString sInfo;
                sInfo.Format( _T("\\StringFileInfo\\%04x%04x\\FileVersion"), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage );

                if( !::VerQueryValue(pFileVersionBuffer, sInfo.GetBuffer( sInfo.GetLength() ), (void**) &pszInfo, (UINT*) &dwSize) )
                    continue;
             
                //Getting Major version
                int iLast = 0;
                int iSplt =  sInfo.Find( ",", iLast );
                Major = sInfo.Left( iSplt );
                iLast = iSplt+1;

                //Getting Minor version
                iSplt = sInfo.Find( ",", iLast );
                for(int i = iLast; i<iSplt; i++)
                    Minor += sInfo.GetAt(i);
                iLast = iSplt+1;

                //Getting Revision Number
                iSplt = sInfo.Find( ",", iLast );
                for( i = iLast; i<iSplt; i++ )
                    Revision += sInfo.GetAt(i);
                iLast = iSplt+1;

                //Getting Build Number
                Build = sInfo.Right( sInfo.GetLength() - iLast );
                
                dwMajor = _ttol(Major);
                dwMinor = _ttol(Minor);
                dwRevision = _ttol(Revision);
                dwBuild = _ttol(Build);
             
                break;
            }
        }
        else
            return FALSE;
    }
    else
        return FALSE;

  if( pdwMaj )
    *pdwMaj = dwMajor;

  if( pdwMin )
      *pdwMin = dwMinor;

  if( pdwRev )
      *pdwRev = dwRevision;

  if( pdwBuild )
      *pdwBuild = dwBuild;
    
    return TRUE;
}

BOOL PASCAL File::NameFromFullPath( LPCOLESTR lpstrFullPath, OLECHAR szName[MAX_PATH] )
{
    if( lpstrFullPath || wcslen(lpstrFullPath) == 0 )
        return FALSE;

    WCHAR drive[_MAX_DRIVE];
    WCHAR dir[_MAX_PATH];
    WCHAR fname[_MAX_FNAME];
    WCHAR ext[_MAX_EXT];
    
    _wsplitpath( lpstrFullPath, drive, dir, fname, ext );

    wcscpy( szName, fname );
    wcscat( szName, ext );

    return TRUE;
}

DWORD PASCAL File::CheckSum( LPCOLESTR lpstrFullPath )
{
    if( !lpstrFullPath || wcslen(lpstrFullPath) == 0 )
        return 0;
#define CHK_BUF_LEN    1024

    File cwf;
    if( !cwf.Open( lpstrFullPath, 0 ) )
        return 0;

    DWORD dwCheckSum = 0;

    LPBYTE pBuffer = new BYTE[CHK_BUF_LEN];
    if( !pBuffer )
        return 0;

    UINT iActuallyRead = 0;

    while( iActuallyRead = cwf.Read( pBuffer, CHK_BUF_LEN ) )
    {
        for( UINT i = 0; i < iActuallyRead; i++ )
            dwCheckSum += pBuffer[i];
    } 
    
    delete []pBuffer;

    return dwCheckSum;
}
*/